#include <gce_MakeParab.hxx>
#include <GC_MakeArcOfCircle.hxx>
#include <GC_MakeArcOfEllipse.hxx>
#include <GC_MakeArcOfHyperbola.hxx>
#include <GC_MakeArcOfParabola.hxx>
#include <GC_MakeCircle.hxx>
#include <GC_MakeEllipse.hxx>
#include <GC_MakeHyperbola.hxx>
#include <GC_MakeSegment.hxx>

#include "CommandConstraints.h"
#include "Base/Console.h"
#include "Base/Tools2D.h"
#include "Part/App/Geometry.h"
using namespace Sketcher;
namespace SketcherGui {
    Part::GeomPoint* create_point(double x, double y) {
        return new Part::GeomPoint(Base::Vector3d(x,y,0.0));
    }
    Part::GeomLineSegment* create_segment(const Base::Vector2d& v21, const Base::Vector2d& v22) {
        Part::GeomLineSegment* line = new Part::GeomLineSegment();
        try {
            Base::Vector3d v1 = Base::Vector3d(v21.x, v21.y, 0.0);
            Base::Vector3d v2 = Base::Vector3d(v22.x, v22.y, 0.0);
            // Create line out of two points
            double distance = Base::Distance(v1, v2);
            if (distance < gp::Resolution())
                Standard_Failure::Raise("Both points are equal");
            GC_MakeSegment ms(gp_Pnt(v1.x, v1.y, v1.z),
                              gp_Pnt(v2.x, v2.y, v2.z));
            if (!ms.IsDone()) {
                delete line;
                return nullptr;
            }

            // get Geom_Line of line segment
            Handle(Geom_TrimmedCurve) this_curv = Handle(Geom_TrimmedCurve)::DownCast
                (line->handle());
            Handle(Geom_Line) this_line = Handle(Geom_Line)::DownCast
                (this_curv->BasisCurve());
            Handle(Geom_TrimmedCurve) that_curv = ms.Value();
            Handle(Geom_Line) that_line = Handle(Geom_Line)::DownCast(that_curv->BasisCurve());
            this_line->SetLin(that_line->Lin());
            this_curv->SetTrim(that_curv->FirstParameter(), that_curv->LastParameter());

            return line;
        } catch (Standard_Failure) {
            delete line;
            return nullptr;
        }
    }

    const Part::GeomArcOfCircle* createArcOfCircle(const Base::Vector2d& center, double radius, double start_angle, double end_angle) {
        // 创建三维坐标系（XY平面，Z轴方向）
        const gp_Ax2 axis(gp_Pnt(center.x, center.y, 0), gp_Dir(0, 0, 1));

        // 使用传入参数构造圆
        Handle(Geom_Circle) circle = new Geom_Circle(axis, radius);
        GC_MakeArcOfCircle arc(circle->Circ(), start_angle, end_angle, Standard_True);
        if (!arc.IsDone()) {
            return nullptr;
        }
        Part::GeomArcOfCircle* gem_arc = new Part::GeomArcOfCircle();
        gem_arc->setHandle(arc.Value());
        return gem_arc;
    }


    const Part::GeomCircle* create_circle(const Base::Vector2d& center, double radius) {
        GC_MakeCircle mc(gp_Pnt(center.x, center.y, 0.0),
                         gp_Dir(0, 0, 1),
                         radius);
        const gp_Ax2 axis(gp_Pnt(center.x, center.y, 0), gp_Dir(0, 0, 1));
        Handle(Geom_Circle) circle = new Geom_Circle(gp_Circ());
        circle->SetCirc(mc.Value()->Circ());
        return new Part::GeomCircle(circle);
    }


    Part::GeomEllipse* create_elliopse(const Base::Vector2d& v1, const Base::Vector2d& v2, const Base::Vector2d& v3) {
        GC_MakeEllipse me(gp_Pnt(v1.x, v1.y, 0.0),
                          gp_Pnt(v2.x, v2.y, 0.0),
                          gp_Pnt(v3.x, v3.y, 0.0));
        if (!me.IsDone()) {
            return nullptr;
        }

        Handle(Geom_Ellipse) ellipse = new Geom_Ellipse(gp_Elips());
        ellipse->SetElips(me.Value()->Elips());
        return new Part::GeomEllipse(ellipse);
    }

    const Part::GeomBSplineCurve* create_bspline(const std::vector<Base::Vector2d>& list, bool cond, int spline_degree) {
        static const char* kwlist[] = {"poles", "mults", "knots", "periodic", "degree", "weights", "CheckRational", nullptr};
        bool periodic = false;
        bool CheckRational = true;
        int degree = 3;
        int number_of_poles = 0;
        int number_of_knots = 0;
        int sum_of_mults = 0;
        try {
            // poles have to be present
            number_of_poles = list.size();
            if ((number_of_poles) < 2) {
                Standard_Failure::Raise("need two or more poles");
                return nullptr;
            }
            TColgp_Array1OfPnt occpoles(1, number_of_poles);
            Standard_Integer index = 1;
            for (auto it = list.begin(); it != list.end(); ++it) {
                occpoles(index++) = gp_Pnt(it->x, it->y, 0.0);
            }

            //Calculate the number of knots
            if (number_of_poles <= degree) {
                degree = number_of_poles - 1;
            }
            number_of_knots = number_of_poles - degree + 1;
            TColStd_Array1OfInteger occmults(1, number_of_knots);
            TColStd_Array1OfReal occknots(1, number_of_knots);
            TColStd_Array1OfReal occweights(1, number_of_poles);
            //mults are 1 or degree+1 at the ends
            for (int i = 1; i <= occmults.Length(); i++) {
                occmults.SetValue(i, 1);
            }
            if (!periodic && occmults.Length() > 0) {
                occmults.SetValue(1, degree + 1);
                occmults.SetValue(occmults.Length(), degree + 1);
                sum_of_mults = occmults.Length() + 2 * degree;
            } else {
                sum_of_mults = occmults.Length() - 1;
            }

            // check multiplicity of inner knots
            for (Standard_Integer i = 2; i < occmults.Length(); i++) {
                if (occmults(i) > degree) {
                    Standard_Failure::Raise("multiplicity of inner knot higher than degree");
                }
            }
            {
                // knotes are uniformly spaced 0..1 if not given
                for (int i = 1; i <= occknots.Length(); i++) {
                    occknots.SetValue(i, (double)(i - 1) / (occknots.Length() - 1));
                }
            }
            {
                // weights are 1.0
                for (int i = 1; i <= occweights.Length(); i++) {
                    occweights.SetValue(i, 1.0);
                }
            }
            // check if the number of poles matches the sum of mults
            if ((periodic && sum_of_mults != number_of_poles) ||
                (!periodic && sum_of_mults - degree - 1 != number_of_poles)) {
                Standard_Failure::Raise("number of poles and sum of mults mismatch");
                return nullptr;
            }

            Handle(Geom_BSplineCurve) spline = new Geom_BSplineCurve(occpoles, occweights, occknots, occmults, degree,
                                                                     periodic ? Standard_True : Standard_False,
                                                                     CheckRational ? Standard_True : Standard_False);
            if (!spline.IsNull()) {
                Part::GeomBSplineCurve* curve = new Part::GeomBSplineCurve(spline);
                return curve;
            } else {
                Standard_Failure::Raise("failed to create spline");
                return nullptr; // goes to the catch block
            }
        } catch (const Standard_Failure& e) {
            Standard_CString msg = e.GetMessageString();
            Base::Console().Error(std::format("Failed to create B-spline curve: {}", msg).c_str());
            return nullptr;
        }
    }

    Part::GeomArcOfEllipse* createArcOfEllipse(const Base::Vector2d& v1, const Base::Vector2d& v2, const Base::Vector2d& v3, double u1, double u2) {
        GC_MakeEllipse me(gp_Pnt(v1.x, v1.y, 0.0),
                          gp_Pnt(v2.x, v2.y, 0.0),
                          gp_Pnt(v3.x, v3.y, 0.0));
        if (!me.IsDone()) {
            return nullptr;
        }
        Handle(Geom_Ellipse) ellipse = new Geom_Ellipse(gp_Elips());
        ellipse->SetElips(me.Value()->Elips());
        GC_MakeArcOfEllipse arc(ellipse->Elips(), u1, u2, Standard_True);
        if (!arc.IsDone()) {
            return nullptr;
        }
        auto getGeomArcOfEllipsePtr = new Part::GeomArcOfEllipse();
        getGeomArcOfEllipsePtr->setHandle(arc.Value());
        return getGeomArcOfEllipsePtr;
    }


    Part::GeomArcOfHyperbola* createArcOfHyperbola(const Base::Vector2d& pv1, const Base::Vector2d& pv2, const Base::Vector2d& pv3, double u1, double u2) {
        bool sense = true;
        try {
            Base::Vector3d v1 = Base::Vector3d(pv1.x, pv1.y, 0.0);
            Base::Vector3d v2 = Base::Vector3d(pv2.x, pv2.y, 0.0);
            Base::Vector3d v3 = Base::Vector3d(pv3.x, pv3.y, 0.0);
            GC_MakeHyperbola me(gp_Pnt(v1.x, v1.y, v1.z),
                                gp_Pnt(v2.x, v2.y, v2.z),
                                gp_Pnt(v3.x, v3.y, v3.z));
            if (!me.IsDone()) {
                // PyErr_SetString(PartExceptionOCCError, gce_ErrorStatusText(me.Status()));
                return nullptr;
            }

            GC_MakeArcOfHyperbola arc(me.Value()->Hypr(), u1, u2, true ? Standard_True : Standard_False);
            if (!arc.IsDone()) {
                return nullptr;
            }
            auto getGeomArcOfHyperbolaPtr = new Part::GeomArcOfHyperbola();
            getGeomArcOfHyperbolaPtr->setHandle(arc.Value());
            return getGeomArcOfHyperbolaPtr;
        } catch (Standard_Failure& e) {
            return nullptr;
        }
        catch (...) {
            // PyErr_SetString(PartExceptionOCCError, "creation of arc failed");
            return nullptr;
        }
    }

    Part::GeomArcOfParabola* createArcOfParabola(const Base::Vector2d& v1, const Base::Vector2d& v2, double u1, double u2) {
        const bool sense = true;
        try {
            Base::Vector3d focus = Base::Vector3d(v1.x, v1.y, 0.0);
            Base::Vector3d center = Base::Vector3d(v2.x, v2.y, 0.0);
            Base::Vector3d normal = Base::Vector3d(0.0, 0.0, 1.0);

            Base::Vector3d xvect = focus - center;

            // set the geometry
            gp_Pnt p1(center.x, center.y, center.z);
            gp_Dir norm(normal.x, normal.y, normal.z);
            gp_Dir xdiroce(xvect.x, xvect.y, xvect.z);

            gp_Ax2 xdir(p1, norm, xdiroce);

            gce_MakeParab mc(xdir, (Standard_Real)xvect.Length());
            if (!mc.IsDone()) {
                return nullptr;
            }

            Handle(Geom_Parabola) parabola = new Geom_Parabola(gp_Parab());
            parabola->SetParab(mc.Value());
            GC_MakeArcOfParabola arc(parabola->Parab(), u1, u2, true ? Standard_True : Standard_False);
            if (!arc.IsDone()) {
                return nullptr;
            }
            auto getGeomArcOfParabolaPtr = new Part::GeomArcOfParabola();
            getGeomArcOfParabolaPtr->setHandle(arc.Value());
            return getGeomArcOfParabolaPtr;
        } catch (Standard_Failure& e) {
            return nullptr;
        }
        catch (...) {
            // PyErr_SetString(PartExceptionOCCError, "creation of arc failed");
            return nullptr;
        }
    }
    Sketcher::Constraint * create_TangentViaPoint(int d1,int d2,int d3,int d4) {
        Constraint* constraint = new Constraint();
        constraint->Type = ConstraintType::Tangent;
        constraint->First     = d1;
        constraint->FirstPos  = Sketcher::PointPos::none;
        constraint->Second    = d2;
        constraint->SecondPos = Sketcher::PointPos::none;
        constraint->Third     = d3;
        constraint->ThirdPos  = (Sketcher::PointPos) d4;
        return constraint;
    }
    Sketcher::Constraint * create_PerpendicularViaPoint(int d1,int d2,int d3,int d4) {
        Constraint* constraint = new Constraint();
        constraint->Type = ConstraintType::Perpendicular;
        constraint->First     = d1;
        constraint->FirstPos  = Sketcher::PointPos::none;
        constraint->Second    = d2;
        constraint->SecondPos = Sketcher::PointPos::none;
        constraint->Third     = d3;
        constraint->ThirdPos  = (Sketcher::PointPos) d4;
        return constraint;
    }
    Sketcher::Constraint * create_AngleViaPoint(int intArg1,int intArg2,int intArg3,int intArg4,double Value) {
        Constraint* constraint = new Constraint();
        constraint->Type = ConstraintType::Angle;
        constraint->First     = intArg1;
        constraint->FirstPos  = Sketcher::PointPos::none;
        constraint->Second    = intArg2; //let's goof up all the terminology =)
        constraint->SecondPos = Sketcher::PointPos::none;
        constraint->Third     = intArg3;
        constraint->ThirdPos  = (Sketcher::PointPos) intArg4;
        constraint->setValue(Value);
        return constraint;
    }
   
}
